package com.wz.dhs.service.base.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wz.dhs.constant.RedisKeyConstant;
import com.wz.dhs.dto.user.LoginDTO;
import com.wz.dhs.dto.user.RegisterDTO;
import com.wz.dhs.exception.BusinessException;
import com.wz.dhs.mapper.user.SysUserMapper;
import com.wz.dhs.model.UserTokenCache;
import com.wz.dhs.model.common.BusinessCode;
import com.wz.dhs.po.user.SysUserEntity;
import com.wz.dhs.service.base.UserService;
import com.wz.dhs.utils.JWTTokenUtil;
import com.wz.dhs.utils.RedisUtils;
import com.wz.dhs.vo.login.LoginUserDetail;
import com.wz.dhs.vo.login.Oauth2TokenVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * @param
 * @author xia
 * @Description:
 * @date 2021/1/24
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<SysUserMapper, SysUserEntity> implements UserService {

    @Resource
    private AuthenticationManager authenticationManager;


    @Resource
    private PasswordEncoder passwordEncoder;


    /**
     * 用户登录
     * LoginDTO loginDTO
     *
     * @return Map
     */
    public Oauth2TokenVO login(LoginDTO param) {

        String phone = param.getPhone();
        String password = param.getPassword();
        UsernamePasswordAuthenticationToken authentication =
                new UsernamePasswordAuthenticationToken(
                        phone,
                        password);
        Authentication authenticate = null;
        try {
            authenticate = authenticationManager.authenticate(authentication);
        } catch (BadCredentialsException e) {
            throw new BusinessException(BusinessCode.USER_PASSWORD_ERROR.getCode());
        } catch (DisabledException e) {
            throw new BusinessException(BusinessCode.USER_DISABLED.getCode());
        }
        if (Objects.isNull(authenticate)) {
            throw new BusinessException(BusinessCode.USER_PASSWORD_ERROR.getCode());
        }
        // 认证成功
        LoginUserDetail loginUser = (LoginUserDetail) authenticate.getPrincipal();

        String token = JWTTokenUtil.createAccessToken(loginUser.getSysUserEntity().getId());
        UserTokenCache user = new UserTokenCache();
        BeanUtils.copyProperties(loginUser.getSysUserEntity(), user);
        user.setUserId(Long.valueOf(loginUser.getSysUserEntity().getId()));
        String key = String.format(RedisKeyConstant.USER_TOKEN_PREFIX, token);
        // 将用户信息存入redis
        LocalDateTime expireTime = LocalDateTime.now().plusDays(15);
        RedisUtils.setValueWithExpire(key, user, TimeUnit.DAYS, 15L);
        Oauth2TokenVO oauth2TokenVO = new Oauth2TokenVO();
        oauth2TokenVO.setUserId(loginUser.getSysUserEntity().getId());
        oauth2TokenVO.setPhone(loginUser.getSysUserEntity().getPhone());
        oauth2TokenVO.setUsername(loginUser.getSysUserEntity().getUsername());
        oauth2TokenVO.setExpiresIn(expireTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
        oauth2TokenVO.setToken(token);
        return oauth2TokenVO;
    }

    @Override
    public void logout(String token) {
        String key = String.format(RedisKeyConstant.USER_TOKEN_PREFIX, token);
        RedisUtils.delKey(key);
    }





    @Override
    public void userRegister(RegisterDTO param) {
        Long count = this.lambdaQuery()
                .eq(SysUserEntity::getPhone, param.getPhone())
                .count();
        if (count > 0) {
            throw new BusinessException(BusinessCode.PHONE_EXIST.getCode());
        }
        if (!param.getPassword().equals(param.getRepeatPassword())) {
            throw new BusinessException(BusinessCode.PASSWORD_ERROR.getCode());
        }

        String validKey = String.format(RedisKeyConstant.APP_CODE_PHONE_PREFIX, 1, param.getPhone());
        if (!RedisUtils.hasKey(validKey) || !param.getValidCode().equals(RedisUtils.getValueOfString(validKey))) {
            throw new BusinessException(BusinessCode.VALID_CODE_ERROR.getCode());
        }
        RedisUtils.delKey(validKey);
        this.save(SysUserEntity.builder()
                .phone(param.getPhone())
                .username(Objects.isNull(param.getUsername()) ? "用户" + StringUtils.substring(param.getPhone(), 7) : param.getUsername())
                .password(param.getPassword())
                .passwordEncry(passwordEncoder.encode(param.getPassword()))
                .accountStatus(true).build());
        log.info("[注册成功][{}]", param.getPhone());
    }


}
